package com.taugin.wechatreader.business;

import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;

import com.taugin.wechatreader.MainActivity;
import com.taugin.wechatreader.info.WxAppInfo;
import com.taugin.wechatreader.info.WxContactInfo;
import com.taugin.wechatreader.utils.Log;
import com.taugin.wechatreader.utils.Utils;

import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

/**
 * Created by ck01- on 2017/6/15.
 */

public class WxReaderBusiness implements IBusiness, Runnable {

    public static final String WX_PKGNAME = "com.tencent.mm";
    public static final String AUTH_INFO_KEY_PREFS = "auth_info_key_prefs.xml";
    public static final String WX_ROOT_PATH = "/data/data/" + WX_PKGNAME;
    public static final String WX_MICROMSG_PATH = new File(WX_ROOT_PATH, "MicroMsg").getAbsolutePath();
    public static final String WX_ENMICROMSG_FILE = "EnMicroMsg.db";

    private OnWxReaderListener mOnWxReaderListener;
    private Context mContext;
    private SQLiteDatabase mSQLiteDatabase;


    private SQLiteDatabaseHook mDBHook = new SQLiteDatabaseHook() {
        public void preKey(SQLiteDatabase database) {
        }

        public void postKey(SQLiteDatabase database) {
            database.rawExecSQL("PRAGMA cipher_migrate;"); //兼容2.0的数据库
        }
    };

    public WxReaderBusiness() {
    }

    @Override
    public void checkEnv() {
        WxAppInfo wxAppInfo = Utils.getWxAppInfo(mContext);
        if (wxAppInfo == null) {
            if (mOnWxReaderListener != null) {
                mOnWxReaderListener.onInitFailure("没有安装微信");
                return;
            }
        }
        if (!Utils.isRoot()) {
            if (mOnWxReaderListener != null) {
                mOnWxReaderListener.onInitFailure("没有Root权限");
                return;
            }
        }
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.onInitSuccess();
        }
    }

    @Override
    public void connectDatabase() {
        // 开始连接数据库线程
        if (mSQLiteDatabase != null && mSQLiteDatabase.isOpen()) {
            mSQLiteDatabase.close();
            mSQLiteDatabase = null;
        }
        new Thread(this).start();
    }

    @Override
    public void readContacts() {
        showProgressMsg("读取联系人");
        if (mSQLiteDatabase == null) {
            error("数据库未打开");
            return;
        }
        String sql = null;
        sql = "select * from rcontact where verifyFlag = 0 and type != 4 and type != 2 and nickname != '' limit 20, 9999";
        sql = MainActivity.SQL_QUERY_CONTACT_AND_IMG;
        List<WxContactInfo> list = new ArrayList<WxContactInfo>();
        try {
            Cursor c = mSQLiteDatabase.rawQuery(sql, null);
            if (c != null && c.moveToFirst()) {
                WxContactInfo info = null;
                do {
                    info = new WxContactInfo();
                    String userName = c.getString(c.getColumnIndex("username"));
                    info.setUserName(userName);
                    String alias = c.getString(c.getColumnIndex("alias"));
                    info.setAlias(alias);
                    String nickName = c.getString(c.getColumnIndex("nickname"));
                    info.setNickName(nickName);
                    info.setIcon(c.getString(c.getColumnIndex("reserved1")));
                    info.setIcon2(c.getString(c.getColumnIndex("reserved2")));
                    list.add(info);
                } while (c.moveToNext());
                c.close();
            }
        } catch (Exception e) {
            error("打开数据库错误 ：" + e);
        }
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.onReadContact(list);
        }
    }

    @Override
    public void setOnWxReaderListener(OnWxReaderListener l) {
        mOnWxReaderListener = l;
    }

    @Override
    public void setContext(Context context) {
        mContext = context;
        SQLiteDatabase.loadLibs(mContext);
    }

    private void error(String msg) {
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.error(msg);
        }
    }

    @Override
    public void onDestroy() {
        if (mSQLiteDatabase != null && mSQLiteDatabase.isOpen()) {
            mSQLiteDatabase.close();
            mSQLiteDatabase = null;
        }
    }

    private boolean connectStoredDatabases() {
        showProgressMsg("连接数据库");
        String storedDB = Utils.getString(mContext, "wx_database");
        String password = Utils.getString(mContext, "password");
        if (TextUtils.isEmpty(storedDB) || TextUtils.isEmpty(password)) {
            return false;
        }
        File f = new File(storedDB);
        try {
            mSQLiteDatabase = openDatabase(f, password);
            if (mSQLiteDatabase != null) {
                return true;
            }
        }catch(Exception e) {
            f.delete();
        }
        Utils.putString(mContext, "wx_database", null);
        Utils.putString(mContext, "password", null);
        return false;
    }

    @Override
    public void run() {
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.onConnectDBStart();
        }
        if (connectStoredDatabases()) {
            if (mOnWxReaderListener != null) {
                mOnWxReaderListener.onConnectDBEnd(true);
            }
            return;
        }

        if (!addFindCommand()) {
            error("无法找到find命令");
            return;
        }

        List<String> list = findEnMicroMsgPath();
        Log.d(Log.TAG, "list : " + list);
        if (list == null || list.isEmpty()) {
            error("没有找到EnMicroMsg.db");
            return;
        }
        //////
        if (!copyNeedFiles(list)) {
            error("拷贝文件到工作区失败");
            return;
        }

        String uin = parseAuthPrefs();
        Log.d(Log.TAG, "uin : " + uin);
        if (TextUtils.isEmpty(uin)) {
            error("没有找到uin");
            return;
        }

        String imei = getImei();
        Log.d(Log.TAG, "imei : " + imei);
        if (TextUtils.isEmpty(uin)) {
            error("无法获取imei");
            return;
        }
        final String password = generatePassword(imei, uin);
        if (TextUtils.isEmpty(password)) {
            error("无法计算计算SQLCIPHER密码");
            return;
        }

        connectDatabaseInternal(password);
    }

    private void connectDatabaseInternal(final String password) {
        mSQLiteDatabase = connectDatabase(password);
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.onConnectDBEnd(mSQLiteDatabase != null);
        }
    }

    private void showProgressMsg(String msg) {
        if (mOnWxReaderListener != null) {
            mOnWxReaderListener.showProgressMsg(msg);
        }
    }

    private String getFindCmd() {
        return new File(mContext.getFilesDir(), "find").getAbsolutePath();
    }
    /**
     * 增加linux find命令
     * @return
     */
    private boolean addFindCommand() {
        showProgressMsg("拷贝find文件");
        Utils.copyAssets(mContext, "find", getFindCmd());
        Utils.execCmd("chmod 777 " + getFindCmd());
        return true;
    }

    /**
     * 查找查找EnMicroMsg.db文件
     * @return
     */
    private List<String> findEnMicroMsgPath() {
        showProgressMsg("查找EnMicroMsg.db");
        List<String> list = new ArrayList<String>();
        StringBuilder out = new StringBuilder();
        StringBuilder error = new StringBuilder();
        Utils.execRootCmd(getFindCmd() + " " + WX_MICROMSG_PATH + " -name " + WX_ENMICROMSG_FILE, out, error);
        Log.d(Log.TAG, "out : " + out.toString());
        Log.d(Log.TAG, "error : " + error.toString());
        if (out.length() > 0) {
            BufferedReader br = new BufferedReader(new StringReader(out.toString()));
            String line = null;
            try {
                while((line = br.readLine()) != null) {
                    list.add(line);
                }
                br.close();
            } catch (Exception e) {
                Log.d(Log.TAG, "error : " + e);
            }
        }
        return list;
    }

    /**
     * 返回微信工作区目录
     * @return
     */
    private String findWxDBDir() {
        File wxDBDir = null;
        wxDBDir = mContext.getExternalFilesDir("wxdb");
        if (wxDBDir == null) {
            wxDBDir = new File(mContext.getFilesDir(), "wxdb");
        }
        return wxDBDir.getAbsolutePath();
    }

    private void copyEnMicroMsgFiles(List<String> list) {
        String wxDBDir = findWxDBDir();
        String wxDBName = "EnMicroMsg";
        if (list != null) {
            for(int index = 0; index < list.size(); index++) {
                String f = list.get(index);
                String cmd = "cp " + f + " " + new File(wxDBDir, wxDBName + index + ".db").getAbsolutePath();
                StringBuilder out = new StringBuilder();
                StringBuilder error = new StringBuilder();
                Utils.execRootCmd(cmd, out, error);
                Log.d(Log.TAG, "out : " + out.toString());
                Log.d(Log.TAG, "error : " + error.toString());
            }
        }
    }

    private void copyAuthPrefs(String authPrefs) {
        String wxDBDir = findWxDBDir();
        String toFile = new File(wxDBDir, AUTH_INFO_KEY_PREFS).getAbsolutePath();
        Log.d(Log.TAG, "toFile : " + toFile);
        String cmd = "cat " + authPrefs;
        StringBuilder out = new StringBuilder();
        StringBuilder error = new StringBuilder();
        Utils.execRootCmd(cmd, out, error);
        Log.d(Log.TAG, "out : " + out.toString());
        Log.d(Log.TAG, "error : " + error.toString());
        if (out.length() > 0) {
            try {
                FileOutputStream fos = new FileOutputStream(toFile);
                fos.write(out.toString().getBytes());
                fos.close();
            } catch(Exception e) {
            }
        }
    }

    private String findAuthInfoKeyPrefs() {
        List<String> list = new ArrayList<String>();
        StringBuilder out = new StringBuilder();
        StringBuilder error = new StringBuilder();
        Utils.execRootCmd(getFindCmd() + " " + WX_ROOT_PATH + " -name " + AUTH_INFO_KEY_PREFS, out, error);
        return out.toString();
    }

    /**
     * 拷贝文件到工作区
     * @param list
     * @return
     */
    private boolean copyNeedFiles(List<String> list) {
        showProgressMsg("拷贝EnMicroMsg.db");
        copyEnMicroMsgFiles(list);
        showProgressMsg("查找auth_info_key_prefs.xml");
        String authPrefs = findAuthInfoKeyPrefs();
        Log.d(Log.TAG, "authPrefs : " + authPrefs);
        if (TextUtils.isEmpty(authPrefs)) {
            error("没有找到auth_info_key_prefs.xml");
            return false;
        }
        showProgressMsg("拷贝auth_info_key_prefs.xml");
        copyAuthPrefs(authPrefs);
        return true;
    }

    /**
     * 解析auth_info_key_prefs文件，返回uin
     * @return
     */
    private String parseAuthPrefs() {
        showProgressMsg("解析uin字段");
        try {
            String wxDBDir = findWxDBDir();
            File f = new File(wxDBDir, AUTH_INFO_KEY_PREFS);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(f);
            NodeList nl = doc.getElementsByTagName("int");
            for (int i = 0; i < nl.getLength(); i++) {
                NamedNodeMap map = nl.item(i).getAttributes();
                if (map.getNamedItem("name").getNodeValue().equals("_auth_uin")) {
                    return map.getNamedItem("value").getNodeValue();
                }
            }

        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    /**
     * 读取IMEI
     */
    private String getImei() {
        showProgressMsg("读取IMEI");
        return Utils.getImei(mContext);
    }

    private String generatePassword(String imei, String uin) {
        showProgressMsg("计算SQLCIPHER密码");
        String password = Utils.string2MD5(imei + uin);
        if (password != null && password.length() > 7) {
            password = password.substring(0, 7);
            password = password.toLowerCase(Locale.CHINA);
            return password;
        }
        return null;
    }

    /**
     * 通过密码打开数据库文件
     * @param f
     * @param password
     * @return 打开的数据库对象
     */
    private SQLiteDatabase openDatabase(File f, String password) {
        try {
            showProgressMsg("连接" + f.getName());
            return SQLiteDatabase.openOrCreateDatabase(f, password, null, mDBHook);
        }catch(Exception e) {
            f.delete();
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    private SQLiteDatabase connectDatabase(String password) {
        File dir = new File(findWxDBDir());
        File [] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return file.getName().endsWith(".db");
            }
        });
        SQLiteDatabase sdb = null;
        for (File f : files) {
            Log.d(Log.TAG, "f : " + f);
            if (f.exists()) {
                sdb = openDatabase(f, password);
            }
            if (sdb != null) {
                // 存储连接成功的数据库文件和密码
                Utils.putString(mContext, "password", password);
                Utils.putString(mContext, "wx_database", f.getAbsolutePath());
                break;
            }
        }
        return sdb;
    }
}